#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include <time.h>
#include "client.h"
#include "cl.h"

#define _WRITE_ 1
#define _READ_ 	2
static void siginthandler(int sig);

static int startDB(void);
static int promptForRooms(void);
static int createDB(char * dbname, int roomQty);


int
main(int argc, char * argv[])
{
	int end = 0;
	char * inpbuf;
	
	if ((inpbuf= malloc(MAXLINE+1)) == NULL)
	{
		fprintf(stderr, "Memory allocation error\n");
		return 1;
	}

	system("clear");
	printf("Welcome!\n\n");
	
	if(startDB() == -1)
		exit(1);
		
	signal(SIGINT, siginthandler);
	while (!end && input(PROMPT, inpbuf))
		end = process_line(inpbuf);
	
	free(inpbuf);
	printf("Thanks for using HotelDB!\n\n");
	return 0;
}

static int
startDB(void)
{
	int roomQty;

	if( !access(_HDB_, F_OK) )
		return 0;
	
	while((roomQty= promptForRooms()) == 0);
	
	createDB(_HDB_, roomQty);
	return 0;
}

static int
promptForRooms(void)
{
	char * line;
	char c;
	int roomQty = 0, leave = 0, wrong = 0, count = 0;

	printf("This is the first time you run HotelDB. Please specify the amount "
			"of rooms: ");
	
	if( (line= malloc(MAXLINE)) == NULL )
		return -1;
	
	while(!leave && !wrong)
	{
		c= getchar();
	
		if( count < MAXLINE && isdigit(c))
			line[count++]= c;
		
		if( c == '\n' && count < MAXLINE )
		{
			line[count]= '\0';
			leave = 1;
		}
		if( c == '\n' )
		{
			count= 0;
			leave = 1;
		}
		if(!isdigit(c) && !leave)
		{
			wrong = 1;
			while(getchar() != '\n');
		}
	}
	
	if(leave && !wrong)
	{
		if(strlen(line))
			roomQty = StringToInteger(line);
	}
		
	free(line);
	return roomQty;

	return 10;
}

static int
createDB(char * dbname, int roomQty)
{
	int db, i, num;
	headerT header;
	roomT room;
	
	header.roomsQty= roomQty;
	header.rRooms= 0;
	header.oRooms= 0;

	room.status= _FREE_;

	if ((db= open(dbname, O_RDWR | O_CREAT | O_EXCL, 0770)) == -1)
	{
		perror("Can't open DB file");
		return -1;
	}
	
	if( write(db, &header, sizeof(headerT)) < sizeof(headerT))
	{
		perror("Can't write header to new DB file");
		return -1;
	}
	
	room.status = 0;
	room.guest[0] = '\0';
	for( i= 0; i < roomQty; i++ )
		if( write(db, &room, sizeof(roomT)) < sizeof(roomT))
		{
			perror("Can't write header to new DB file");
			return -1;
		}
		
	close(db);
	return 1;
}

int
liberar(char * guest, char * cnumber, char * dbname)
{
	int db, i, number;
	headerT head;
	roomT room;
	
	//printf("LIBERAR guest: %s numero: %s\n", guest, cnumber);
	
	if(guest == NULL || dbname == NULL || cnumber == NULL)
	{
		fprintf(stderr, "Can't free room, inconsistent parameters.\n");
		return -1;
	}
	
	if ((db = open(dbname, O_RDWR)) == -1)
	{
		perror("Can't open DB file");
		return -1;
	}
	
	number = atoi(cnumber);
	if(lockFile(db, _WRITE_ , number) == -1)
	{
		perror("Can't lock DB file");
		return -1;
	}
	
	
	
	if (read(db, &head, sizeof(headerT)) < sizeof(headerT))
	{
		perror("Incosistent DB file");
		return -1;
	}
	
	if(head.rRooms + head.oRooms == 0)
	{
		printf("There are no occupied or booked rooms.\n");
		return 0;
	}
	
	lseek(db, sizeof(headerT) + (number-1) * sizeof(roomT), SEEK_SET);
	
	if (read(db, &room, sizeof(roomT)) < sizeof(roomT))
	{
		perror("Incosistent DB file");
		return -1;
	}
	
	if(room.status == _OCCUPIED_ && !strcmp(room.guest, guest))
	{
		time_t rawtime;
		char * left;
		char occup[60];
		
		time(&rawtime);
		left = ctime(&rawtime);
		strcpy(occup, room.occupied);
		
		room.status = _FREE_;
		room.guest[0] = '\0';
		
		lseek(db, sizeof(headerT) + (number-1) * sizeof(roomT), SEEK_SET);
		
		if( write(db, &room, sizeof(roomT)) < sizeof(roomT))
		{
			perror("Error occupying new room in the DB file");
			return -1;
		}
		
		head.oRooms--;
		lseek(db, 0, SEEK_SET);
		if( write(db, &head, sizeof(headerT)) < sizeof(headerT))
		{
			perror("Error occupying new room in the DB file");
			return -1;
		}
		
		printf("Guest %s has left room %d at %s having arrived at %s", guest, 
							number, left, occup);
	}
	else
		printf("The room is not occupied by the required guest\n");
	
	unlockFile(db, 0);
		
	close(db);
	return 1;
}

int
ocupar(char * guest, char * dbname, char * cnumber)
{
	int db, i, number;
	headerT head;
	roomT room;
	
	//printf("OCUPAR guest: %s numero: %s\n", guest, number);
	
	if(guest == NULL || dbname == NULL || cnumber == NULL)
	{
		fprintf(stderr, "Can't occupy requested room.\n");
		return -1;
	}
	
	if ((db = open(dbname, O_RDWR)) == -1)
	{
		perror("Can't open DB file");
		return -1;
	}
	
	number = atoi(cnumber);
	
	if(lockFile(db, _WRITE_ , number) == -1)
	{
		perror("Can't lock DB file");
		return -1;
	}
	
	
	
	if (read(db, &head, sizeof(headerT)) < sizeof(headerT))
	{
		perror("Incosistent DB file");
		return -1;
	}
	
	if(head.roomsQty == head.oRooms)
	{
		printf("There are no available rooms to occupy at the moment.\n");
		return 0;
	}
	
	lseek(db, sizeof(headerT) + (number-1) * sizeof(roomT), SEEK_SET);
	
	if (read(db, &room, sizeof(roomT)) < sizeof(roomT))
	{
		perror("Incosistent DB file");
		return -1;
	}
	//printf("status: %d \t guest: %s\n", room.status, room.guest);
	if(room.status == _BOOKED_ && !strcmp(room.guest, guest))
	{
		time_t rawtime;
		
		room.status = _OCCUPIED_;
		time(&rawtime);
		strcpy(room.occupied, ctime(&rawtime));
		
		lseek(db, sizeof(headerT) + (number-1) * sizeof(roomT), SEEK_SET);
		
		if( write(db, &room, sizeof(roomT)) < sizeof(roomT))
		{
			perror("Error occupying new room in the DB file");
			return -1;
		}
		
		head.rRooms--;
		head.oRooms++;
		lseek(db, 0, SEEK_SET);
		if( write(db, &head, sizeof(headerT)) < sizeof(headerT))
		{
			perror("Error occupying new room in the DB file");
			return -1;
		}
		
		printf("Room %d was occupied by guest %s at %s", number,
											 guest, room.occupied);
	}
	else
		printf("The room is not booked for the required guest\n");
		
	unlockFile(db, 0);
		
	close(db);		
	return 1;
}

int
reservar(char * guest, char * dbname)
{
	int db, reserved = 0, i;
	headerT head;
	roomT room;
	
	//printf("RESERVAR guest: %s\t db:%s\n", guest, dbname);
	if(guest == NULL || dbname == NULL)
	{
		fprintf(stderr, "Can't make new reservation\n");
		return -1;
	}
	
	if ((db = open(dbname, O_RDWR)) == -1)
	{
		perror("Can't open DB file");
		return -1;
	}
	
	if(lockFile(db, _WRITE_ , 0) == -1)
	{
		perror("Can't lock DB file");
		return -1;
	}
	
	if (read(db, &head, sizeof(headerT)) < sizeof(headerT))
	{
		perror("Incosistent DB file");
		return -1;
	}
	//printf("qty: %d oroom: %d rRoom: %d \n", head.roomsQty, head.oRooms,head.rRooms);
	if(head.roomsQty == head.oRooms + head.rRooms)
	{
		printf("There are no available rooms to make a reservation.\n");
		return 0;
	}
	
	for(i = 0; !reserved && i < head.roomsQty; i++)
	{
		if (read(db, &room, sizeof(roomT)) < sizeof(roomT))
		{
			perror("Incosistent DB file");
			return -1;
		}
		//printf("room.status: %d\n", room.status);
		if(room.status == _FREE_)
		{
			room.status = _BOOKED_;
			strcpy(room.guest, guest);
			lseek(db, sizeof(headerT) + i * sizeof(roomT), SEEK_SET);
			
			if( write(db, &room, sizeof(roomT)) < sizeof(roomT))
			{
				perror("Error booking new room in the DB file");
				return -1;
			}
			
			head.rRooms++;
			
			lseek(db, 0, SEEK_SET);
			if( write(db, &head, sizeof(headerT)) < sizeof(headerT))
			{
				perror("Error booking new room in the DB file");
				return -1;
			}
			
			printf("Room %d was reserverd for guest %s\n", i+1, guest);
			reserved = 1;
		}
		
	}
	
	unlockFile(db, 0);
	
	close(db);
	
	return 1;
}

int
listar(char * guest, char * dbname)
{
	int db, i;
	headerT head;
	roomT room;
	//printf("LISTAR guest: %s  dbname:%s\n", guest, dbname);

	if(guest == NULL || dbname == NULL)
	{
		fprintf(stderr, "Can't list rooms.\n");
		return -1;
	}
	
	if ((db = open(dbname, O_RDWR)) == -1)
	{
		perror("Can't open DB file");
		return -1;
	}
	
	if(lockFile(db, _READ_ , 0) == -1)
	{
		perror("Can't lock DB file");
		return -1;
	}
	
	if (read(db, &head, sizeof(headerT)) < sizeof(headerT))
	{
		perror("Incosistent DB file");
		return -1;
	}
	//printf("qty: %d oroom: %d rRoom: %d \n", head.roomsQty, head.oRooms,head.rRooms);
	if( !head.oRooms && !head.rRooms)
	{
		printf("There are reserved or occupied rooms at the moment.\n");
		return 0;
	}
	printf("List for guest %s:\n", guest);
	
	for(i = 0; i < head.roomsQty; i++)
	{
		if (read(db, &room, sizeof(roomT)) < sizeof(roomT))
		{
			perror("Incosistent DB file");
			return -1;
		}
		
		if(!strcmp(room.guest, guest))
		{
			printf("Room %d %s\n", i+1, room.status == _BOOKED_ ?
									 "BOOKED" : "OCCUPPIED");
		}
		
	}
	
	unlockFile(db, 0);
	
	close(db);
	
	return 1;
}


/* Extra functions */
static void 
siginthandler(int sig)
{
	printf("\nExit!\n");
	exit(1);
}

int
lockFile(int fd, int rorw, int room)
{
	struct flock my_lock;
	
	if(fd < 0 || (rorw != _WRITE_ && rorw != _READ_) || room < 0)
		return -1;
	
	my_lock.l_type = F_WRLCK;
	my_lock.l_whence = SEEK_SET;
	my_lock.l_start = 0;
	my_lock.l_len = sizeof(headerT);
	
	if(fcntl(fd, F_SETLKW, &my_lock) == -1)
	{
		perror("Can't lock DB file");
		return -1;
	}
	
	my_lock.l_type = rorw == _WRITE_ ? F_WRLCK : F_RDLCK;
	my_lock.l_whence = SEEK_SET;
	my_lock.l_start =  room == 0 ? sizeof(headerT) : sizeof(headerT) + 
											(room - 1) * sizeof(roomT);
	my_lock.l_len = room == 0 ? 0 : sizeof(roomT);
	
	
	if(fcntl(fd, F_SETLKW, &my_lock) == -1)
	{
		perror("Can't lock DB file");
		return -1;
	}
	
	return 1;
}

int
unlockFile(int fd, int room)
{
	struct flock my_lock;
	
	if(fd < 0 ||  room < 0)
		return -1;
	
	my_lock.l_type = F_UNLCK;
	my_lock.l_whence = SEEK_SET;
	my_lock.l_start = 0;
	my_lock.l_len = sizeof(headerT);
	
	if(fcntl(fd, F_SETLK, &my_lock) == -1)
	{
		perror("Can't lock DB file");
		return -1;
	}
	
	my_lock.l_type =  F_UNLCK;
	my_lock.l_whence = SEEK_SET;
	my_lock.l_start = room == 0 ? sizeof(headerT) : sizeof(headerT) + 
											(room - 1) * sizeof(roomT);
	my_lock.l_len = room == 0 ? 0 : sizeof(roomT);
	
	
	if(fcntl(fd, F_SETLK, &my_lock) == -1)
	{
		perror("Can't lock DB file");
		return -1;
	}
	
	return 1;
}

